﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using Gtk;
using System.Collections.Specialized;
using System.ComponentModel;

namespace XchangeStreamer.OutputSdk.Collections
{
    [Serializable]
    public class ReadOnlyObservableCollection<T> : TreeModelSort, IList<T>
    {
        #region Private Fields

        private static Type[] propTypes;
        private static Dictionary<string, int> propNames;
        private static string typeName;
        private Dictionary<T, TreeIter> iters = new Dictionary<T, TreeIter>();
        private ListStore model;

        #endregion

        #region Constructors

        static ReadOnlyObservableCollection()
        {
            Type collectionType = typeof(T);

            typeName = collectionType.Name;
            var props = collectionType.GetProperties();
            propTypes = new Type[props.Length];
            propNames = new Dictionary<string, int>(props.Length);

            for (int i = 0; i < props.Length; i++)
            {
                propTypes[i] = typeof(string);
                //propTypes[i] = props[i].PropertyType;
                propNames.Add(props[i].Name, i);
            }
        }

        public ReadOnlyObservableCollection(ObservableCollection<T> list)
            : base(Init())
        {
            this.model = (ListStore)this.Model;
            
            foreach (dynamic item in list)
            {
                this.AddToModel(item);
            }
            
            ((INotifyCollectionChanged)list).CollectionChanged += new NotifyCollectionChangedEventHandler(this.HandleCollectionChanged);
        }

        #endregion
        
        #region Private Methods

        private static ListStore Init()
        {
            return new ListStore(propTypes);
        }

        private void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (dynamic item in e.NewItems)
                    {
                        this.AddToModel(item);
                    }
                    break;
                
                case NotifyCollectionChangedAction.Move:
                    break;
                
                case NotifyCollectionChangedAction.Remove:
                    foreach (dynamic item in e.OldItems)
                    {
                        this.RemoveFromModel(item);
                    }
                    break;
                
                case NotifyCollectionChangedAction.Replace:
                    break;
                
                case NotifyCollectionChangedAction.Reset:
                    break;
                
                default:
                    break;
            }
        }

        private void AddToModel(dynamic item)
        {
            string[] values = new string[propNames.Count];
            foreach (var rec in propNames)
            {
                values[rec.Value] = item.GetValue(rec.Key);
            }

            var itr = this.model.AppendValues(values);
            this.iters.Add(item, itr);

            item.PropertyChanged += new PropertyChangedEventHandler(ReadOnlyObservableCollection_PropertyChanged);
        }

        private void RemoveFromModel(dynamic item)
        {
            item.PropertyChanged -= new PropertyChangedEventHandler(ReadOnlyObservableCollection_PropertyChanged);

            TreeIter itr;
            if (this.iters.TryGetValue(item, out itr))
            {
                this.iters.Remove(item);
                this.model.Remove(ref itr);
            }
        }

        private void UpdateModel(dynamic item, string propName)
        {
            TreeIter itr;
            if (this.iters.TryGetValue(item, out itr))
            {
                this.model.SetValue(itr, propNames[propName], item.GetValue(propName));
            }
        }

        void ReadOnlyObservableCollection_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.UpdateModel(sender, e.PropertyName);
        }

        #endregion

        #region IList<T> Members

        public int IndexOf(T item)
        {
            throw new NotImplementedException();
        }

        public void Insert(int index, T item)
        {
            throw new NotImplementedException();
        }

        public void RemoveAt(int index)
        {
            throw new NotImplementedException();
        }

        public T this[int index]
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region ICollection<T> Members

        public void Add(T item)
        {
            throw new NotImplementedException();
        }

        public void Clear()
        {
            throw new NotImplementedException();
        }

        public bool Contains(T item)
        {
            throw new NotImplementedException();
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            throw new NotImplementedException();
        }

        public int Count
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsReadOnly
        {
            get { throw new NotImplementedException(); }
        }

        public bool Remove(T item)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}